/*
 * FlightController.cpp
 *
 *  Created on: Sep 5, 2013
 *      Author: gonprieto
 */

#include "FlightController.h"
#include "../comunication/inbox/FlightInbox.h"
#include "../utils/SimulatorUtils.h"
#include "../comunication/inbox/TowerControlInbox.h"
#include "../comunication/DTO/DTOFlightControlerRequest.h"
#include "../comunication/inbox/FlightControllerInbox.h"

FlightController::FlightController(unsigned int id,int runwaysCount):inbox(SimulatorUtils::getFlighControlerInboxName(id),true),isRunning(true)  {
	this->id = id;
	this->runwaysCount = runwaysCount;

	this->semRunways = new Semaforo(SimulatorUtils::getRunwayName().c_str(), runwaysCount);
}

FlightController::~FlightController() {
	this->semRunways->eliminar();

	delete this->semRunways;

	//Cierro y elimino el inbox, ya no pueden llegar mas mensajes
	this->inbox.cerrar();
	this->inbox.eliminar();

}

int FlightController::tomarRunway(){
	for (int i = 0; i < runwaysCount; i++) {
		if(semRunways->getValor(i) >= 1){
			log << "FlightController::tomarRunway - Hay libre! "<< eol;
			semRunways->p(i);
			return i;
		}
	}

	log << "FlightController::tomarRunway - Empiezo espera!.Estan todas ocupadas, elijo una y espero "<< eol;
	int runwayId= SimulatorUtils::random_in_range(this->runwaysCount);
	semRunways->p(runwayId);

	log << "FlightController::tomarRunway - Termine espera!"<< eol;
	return runwayId;
}

int FlightController::run() {
	log << "FlightController - Running the controler pid:" << getpid()<<eol ;

	while (this->isRunning || !this->flightsManejando.empty()) {

		log << "FlightController::run - Leo mensaje a procesar" << eol;
		DTO* dto = inbox.leerDTO();

		if(dto==NULL){
			log << "FlightController::run - Termino el FlightControler" << eol;
			return 0;
		}

		log << "FlightController::run - Lei mensaje a procesar" << eol;

		switch (dto->getType()) {
			case FLIGHT_RELATED:{
					log << "FlightController::run - Mensaje FLIGHT_RELATED" << eol;
					procesarFlightRelated((DTOFlightRelated*) dto);
			}break;
			case FLIGHT_CONTROLER:
					log << "FlightController::run - Mensaje Mensaje FLIGHT_CONTROLER FlightEnProceso:" << (int)this->flightsManejando.size() << eol;
					procesarFlightController((DTOFlightControler*) dto);
				break;
			case RUNWAY_RELATED:
			default:
				log << "ERROR : FlightController::run - Recibi un mensaje que no puedo analizar!" << eol;
				break;
		}

		delete dto;
	}

	log << "FlightController::run - Termino el FlightControler" << eol;

	return 0;
}


DTOFlight FlightController::getVueloPrioritario(){
	log << "FlightController::getVueloPrioritario - Entro" << eol;
	log << "FlightController::getVueloPrioritario - Size : " << (int)this->flightsManejando.size()<< eol;

	//Tomo el mas prioritario
	std::vector<DTOFlight>::iterator flightPrioritario = flightsManejando.begin();

	std::vector<DTOFlight>::iterator flightsIt;
	for (flightsIt = this->flightsManejando.begin();
			flightsIt != flightsManejando.end();
			flightsIt++) {

		if ( (*flightsIt).getState() == IN_THE_AIR){
			log << "FlightController::getVueloPrioritario - Vuelo en el aire, le doy mas prioridad  : " << (*flightsIt).getFlightId() << eol;
			flightPrioritario = flightsIt;
			break;
		}
	}

	DTOFlight toReturn = *flightPrioritario;

	//Quito el vuelo de mi lista de pendientes
	this->flightsManejando.erase(flightPrioritario);
	log << "FlightController::getVueloPrioritario - Sale: "<< toReturn.toString() << " -Nuevo size de vuelos a procesar:" << (int)this->flightsManejando.size()  << eol;

	return toReturn;
}


void FlightController::asignarRunway(unsigned int runwayId){
	//Tomo el mas prioritario
	DTOFlight flightPrioritario = getVueloPrioritario();
	log << "FlightController::asignarRunway - Asigno runway a  : " << flightPrioritario.toString() << eol;

	unsigned int flightId = flightPrioritario.getFlightId();

	//Abro la fifo del vuelo y le mando el mensaje
	FlightInbox* fi = new FlightInbox(SimulatorUtils::getFlighInboxName(flightId));

	log << "FlightController::asignarRunway - Flight Id : " << flightId << eol;

	switch (flightPrioritario.getState()) {
		case LAND_FLIGHT:
				landFlight(flightId,fi,runwayId);
			break;
		case TAKE_OFF_FLIGHT:
				takeOffFlight(flightId,fi,runwayId);
			break;
	}

	//Cierro el inbox de escritura del vuelo
	fi->cerrar();
	delete fi;
}

/*
 * Primer mensaje que deja es su identicacion para que el vuelo lo vuelva
 * a poder identificar
 */
void FlightController::procesarFlightRelated(DTOFlightRelated* dto){
	unsigned int flightId = dto->getDtoFlight().getFlightId();

	log << "FlightController::procesarFlightRelated - Flight Id : " << flightId << " -En controler:" << (int)getpid() << eol;

	//Me guardo el flight
	this->flightsManejando.push_back(dto->getDtoFlight());

	int runwayToUse = tomarRunway();

	asignarRunway(runwayToUse);

}

void FlightController::procesarFlightController(DTOFlightControler* dto){
	log << "FlightController::procesarFlightController - Started" << eol;

	if (dto->isTerminate()){
		log << "FlightController::procesarFlightController - Stopping myself" << eol;
		this->isRunning = false;
	}

	log << "FlightController::procesarFlightController - Finish" << eol;
}

void FlightController::landFlight(unsigned int flightId,FlightInbox* fi,unsigned int runwayId) {
	log << "FlightController::landFlight - Landing flight" << eol;

	DTOFlight dtoFlight(flightId,LANDING,runwayId);

	fi->changeStatus(dtoFlight);
}
void FlightController::takeOffFlight(unsigned int flightId,FlightInbox* fi,unsigned int runwayId) {
	log << "FlightController::takeOffFlight - TakingOff flight" << eol;

	DTOFlight dtoFlight(flightId,TAKING_OFF,runwayId);

	fi->changeStatus(dtoFlight);
}

